﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Threading.Tasks;
using SpielMitMirFinalProject.ModelsGame;
using SpielMitMirFinal.ModelsGame;
using SpielMitMirFinal.Models;
using System.Drawing;
using System.IO;
using System.Web.Mvc;
using SpielMitMirFinalProject.ModelViews;
using System.Web.Routing;
using System.Web.SessionState;
using System.Reflection;

namespace SpielMitMirFinal.SignalR
{
    [HubName("Game")]
    public class GameHub : Hub
    {
        private SpielMitMirEntities db = new SpielMitMirEntities();
        private static bool newGameNeeded = true;

        private static Game newGame = new Game();

        public static class UserHandler
        {
            public static List<OnlinePlayer> usersOnline = new List<OnlinePlayer>();
            public static List<Bot> bots = new List<Bot>();
        }

        #region Norifications

        public void Hello(string userId, int gameId)
        {
            Player playerdb = db.Players.FirstOrDefault(x => x.UserId == userId);
            OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId);
            if (player == null)
            {
                player = new OnlinePlayer();
                player.playerProfileId = playerdb.PlayerId;
                SignalRConnection connection = new SignalRConnection();
                connection.connectionId = Context.ConnectionId;
                if (gameId > 0)
                    connection.gameId = gameId;
                player.connections.Add(connection);
                UserHandler.usersOnline.Add(player);
            }
            else
            {
                SignalRConnection connection = new SignalRConnection();
                connection.connectionId = Context.ConnectionId;
                if (gameId > 0)
                    connection.gameId = gameId;
                UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId).connections.Add(connection);
            }
            if (gameId<0)
            {
                IQueryable<Game> games = db.Games.Where(g => g.Players.Any(p => p.PlayerId == playerdb.PlayerId) && g.WinnerId == null);
                List<string> userTiles = new List<string>();
                List<string> gameTiles = new List<string>();
                foreach (Game game in games)
                {
                    GameTile gameTile = new GameTile();
                    gameTile.image = "/images/" + game.Players.FirstOrDefault(p => p.PlayerId != player.playerProfileId).UserId + ".jpg";
                    Strike lastStrike = game.Strikes.LastOrDefault();
                    if (lastStrike != null)
                    {
                        if (lastStrike.PlayerId == player.playerProfileId)
                        {
                            gameTile.message = "G";
                            gameTile.color = "badge bg-green";
                        }
                        else
                        {
                            gameTile.message = "S";
                            gameTile.color = "badge bg-red";
                        }
                    }
                    else
                    {
                        if (game.Players.First().PlayerId == player.playerProfileId)
                        {
                            gameTile.message = "G";
                            gameTile.color = "badge bg-green";
                        }
                        else
                        {
                            gameTile.message = "S";
                            gameTile.color = "badge bg-red";
                        }
                    }
                    gameTile.gameId = game.GameId;
                    gameTile.myId = userId;
                    gameTile.name = game.Players.FirstOrDefault(p => p.PlayerId != player.playerProfileId).GamerTag;
                    String stringGame = PartialViewToStringHelper.GetRazorViewAsString(gameTile, "~/Views/Shared/Partial/_GameTile.cshtml");
                    gameTiles.Add(stringGame);
                }
                foreach (OnlinePlayer onPlayer in UserHandler.usersOnline.Where(u=> u.playerProfileId!=playerdb.PlayerId))
                {
                    UserTile userTile = new UserTile();
                    userTile.gameId = 0;
                    Player playerDb = db.Players.Find(onPlayer.playerProfileId);
                    userTile.userId = onPlayer.playerProfileId;
                    userTile.image = "/images/" + playerDb.UserId + ".jpg";
                    userTile.name = playerDb.GamerTag;
                    userTile.games = db.Games.Where(g => g.WinnerId == onPlayer.playerProfileId).Count().ToString();
                    string stringUserTile = PartialViewToStringHelper.GetRazorViewAsString(userTile, "~/Views/Shared/Partial/_UserTile.cshtml");
                    userTiles.Add(stringUserTile);
                }
                Clients.Caller.Hello(userTiles, gameTiles, Context.ConnectionId);
            }
        }

        public void Challenge(string myId, int userId)
        {
            OnlinePlayer op=UserHandler.usersOnline.FirstOrDefault(u=>u.playerProfileId==userId);
            foreach (SignalRConnection connection in op.connections)
            {
                Clients.Client(connection.connectionId).Challenged(userId);
            }
        }

        #endregion

       
        #region PreGame

        public void AcceptChallenge(string myId, int opId)
        {
            Player bot = db.Players.FirstOrDefault(x => x.GamerTag == "KimJongFun");
            Player playerdb = db.Players.FirstOrDefault(x => x.UserId == myId);
            Player opPlayerdb = db.Players.FirstOrDefault(x => x.PlayerId == opId);
            Game game = new Game();
            game.Players.Add(playerdb);
            game.Players.Add(opPlayerdb);
            game.Date = DateTime.Now;
            game.State = 0;
            db.Games.Add(game);
            db.SaveChanges();

            GameTile gameTile = new GameTile();
            gameTile.image = "/images/" + myId + ".jpg";
            Strike lastStrike = game.Strikes.LastOrDefault();
            gameTile.message = "G";
            gameTile.color = "badge bg-green";
            gameTile.gameId = game.GameId;
            gameTile.myId = myId;
            gameTile.name = playerdb.GamerTag;
            String stringGame = PartialViewToStringHelper.GetRazorViewAsString(gameTile, "~/Views/Shared/Partial/_GameTile.cshtml");

            OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId);
            OnlinePlayer opPlayer = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == opPlayerdb.PlayerId);
            newGameNeeded = false;
            Clients.Caller.Game(game.GameId, stringGame);
            foreach (var connection in opPlayer.connections)
            {
                Clients.Client(connection.connectionId).Game(game.GameId, stringGame);
            }
            Message(bot.UserId, game.GameId, playerdb.GamerTag + ", You are my number 1!");
            Message(bot.UserId, game.GameId, opPlayerdb.GamerTag + ", Second come, second served!");
        }

        public void NewGame(String userId)
        {
            Player bot = db.Players.FirstOrDefault(x => x.GamerTag == "KimJongFun");

            if (newGameNeeded)
            {
                Player playerdb = db.Players.FirstOrDefault(x => x.UserId == userId);
                Game game = new Game();
                game.Players.Add(playerdb);
                game.Date = DateTime.Now;
                game.State = 0;
                db.Games.Add(game);
                db.SaveChanges();

                GameTile gameTile = new GameTile();
                gameTile.image = "/images/" + playerdb.UserId + ".jpg";
                Strike lastStrike = game.Strikes.LastOrDefault();
                gameTile.message = "G";
                gameTile.color = "badge bg-green";
                gameTile.gameId = game.GameId;
                gameTile.myId = userId;
                gameTile.name = playerdb.GamerTag;
                String stringGame = PartialViewToStringHelper.GetRazorViewAsString(gameTile, "~/Views/Shared/Partial/_GameTile.cshtml");
                

                OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId);
                //UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId).connections.FirstOrDefault(c => c.connectionId == Context.ConnectionId).gameId = game.GameId;
                newGameNeeded = false;
                Clients.Caller.Game(game.GameId, stringGame);

                Message(bot.UserId, game.GameId, playerdb.GamerTag + ", You are my number 1!");
            }
            else
            {

                Game game = db.Games.OrderByDescending(x => x.Date).FirstOrDefault();
                Player playerdb = db.Players.FirstOrDefault(x => x.UserId == userId);
                if (game.Players.FirstOrDefault() == playerdb)
                {
                    Clients.Caller.Game(game.GameId, "youagain");
                }
                else
                {
                    game.Players.Add(playerdb);
                    db.SaveChanges();

                    GameTile gameTile = new GameTile();
                    gameTile.image = "/images/" + game.Players.FirstOrDefault().UserId + ".jpg";
                    Strike lastStrike = game.Strikes.LastOrDefault();
                    gameTile.message = "S";
                    gameTile.color = "badge bg-green";
                    gameTile.gameId = game.GameId;
                    gameTile.myId = userId;
                    gameTile.name = playerdb.GamerTag;
                    String stringGame = PartialViewToStringHelper.GetRazorViewAsString(gameTile, "~/Views/Shared/Partial/_GameTile.cshtml");
                
                    OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId);
                    UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId).connections.FirstOrDefault(c => c.connectionId == Context.ConnectionId).gameId = game.GameId;
                    newGameNeeded = true;
                    Clients.Caller.Game(game.GameId, stringGame);

                    Message(bot.UserId, game.GameId, playerdb.GamerTag+", Second come, second served!");
                }
            }
        }

        public void NewBotGame(String userId)
        {
            Player playerdb = db.Players.FirstOrDefault(x => x.UserId == userId);
            Player bot = db.Players.FirstOrDefault(x => x.GamerTag == "KimJongFun");
            Game game = new Game();
            game.Players.Add(playerdb);
            game.Players.Add(bot);
            game.Date = DateTime.Now;
            game.State = -1;
            db.Games.Add(game);
            db.SaveChanges();
            OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId);
            UserHandler.usersOnline.FirstOrDefault(x => x.playerProfileId == playerdb.PlayerId).connections.FirstOrDefault(c => c.connectionId == Context.ConnectionId).gameId = game.GameId;
            
            GameTile gameTile = new GameTile();
            gameTile.image = "/images/" + bot.UserId + ".jpg";
            Strike lastStrike = game.Strikes.LastOrDefault();
            gameTile.message= (game.GameId % 2 == 0) ? "G" : "S";
            gameTile.color = "badge bg-green";
            gameTile.gameId = game.GameId;
            gameTile.myId = userId;
            gameTile.name = playerdb.GamerTag;
            String stringGame = PartialViewToStringHelper.GetRazorViewAsString(gameTile, "~/Views/Shared/Partial/_GameTile.cshtml");
            Clients.Caller.Game(game.GameId, stringGame);
            Message(bot.UserId, game.GameId, "You chalange me? Puny mortal!");
        }
 
        public void Ready(IList<int> xs, IList<int> ys, int gameId)
        {
            Game game = db.Games.Find(gameId);
            OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(x => x.connections.Any(c=>c.connectionId == Context.ConnectionId));
            if (xs.Count > 0 && ys.Count > 0)
            {
                for (int i = 2; i < xs.Count; i = i + 3)
                {
                    Ship ship = new Ship();
                    ship.GameId = gameId;
                    ship.PlayerId = player.playerProfileId;
                    ship.Number = Convert.ToByte(i / 3);
                    db.Ships.Add(ship);
                    for (int j = 0; j < 3; j++)
                    {
                        ShipPart shipPart = new ShipPart();
                        shipPart.ShipId = ship.ShipId;
                        shipPart.X = xs[i - j];
                        shipPart.Y = ys[i - j];
                        shipPart.Destroyed = false;
                        db.ShipParts.Add(shipPart);
                    }
                    db.SaveChanges();
                }
            }
            if (game.State >= 0)
            {
                game.State++;
                if (game.State == game.Players.Count())
                {
                    foreach (OnlinePlayer Oplayer in UserHandler.usersOnline.Where(x => x.connections.Any(y => y.gameId == gameId)))
                    {
                        SignalRConnection connection = Oplayer.connections.FirstOrDefault(y => y.gameId == gameId);
                        Clients.Client(connection.connectionId).Start(game.GameId);
                    }
                    Player bot = db.Players.FirstOrDefault(x => x.GamerTag == "KimJongFun");
                    Message(bot.UserId, game.GameId, "Fight! Fight for my amusement!");
                   
                }
                db.SaveChanges();
            }
            else
            {
                Player botdb = db.Players.FirstOrDefault(x => x.GamerTag == "KimJongFun");
                Bot bot = new Bot();
                bot.PlaceShip(5);
                bot.gameId = gameId;
                bot.opponentId = player.playerProfileId;
                UserHandler.bots.Add(bot);
                int i = 1;
                List<Ship> ships2 = new List<Ship>();
                List<ShipPart> shipsParts2 = new List<ShipPart>();
                foreach (IGShip igship in bot.myShips)
                {
                    Ship ship = new Ship();
                    ship.GameId = gameId;
                    ship.PlayerId = botdb.PlayerId;
                    ship.Number = Convert.ToByte(i);
                    db.Ships.Add(ship);
                    ships2.Add(ship);
                    foreach (Point point in igship.location)
                    {
                        ShipPart shipPart = new ShipPart();
                        shipPart.ShipId = ship.ShipId;
                        shipPart.X = point.X;
                        shipPart.Y = point.Y;
                        shipPart.Destroyed = false;
                        db.ShipParts.Add(shipPart);
                        shipsParts2.Add(shipPart);
                    }
                    db.SaveChanges();
                }
                //foreach connection call start
                foreach(SignalRConnection connection in player.connections)
                {
                    Clients.Client(connection.connectionId).Start(game.GameId);
                }
                if (gameId % 2 != 0)
                {
                    Point hitPoint = bot.Fire();
                    int x = hitPoint.X;
                    int y = hitPoint.Y;
                    Strike strike = new Strike();
                    strike.PlayerId = botdb.PlayerId;
                    strike.GameId = gameId;
                    strike.Turn = 1;
                    strike.X = Convert.ToByte(x);
                    strike.Y = Convert.ToByte(y);
                    db.Strikes.Add(strike);
                    db.SaveChanges();
                    ShipPart shipPart = db.ShipParts.FirstOrDefault(sp => sp.Ship.GameId == gameId && sp.Ship.PlayerId == player.playerProfileId && sp.X == x && sp.Y == y);
                    bool hit = false;
                    if (shipPart != null)
                    {
                        shipPart.Destroyed = true;
                        db.SaveChanges();
                        hit = true;
                    }
                    bot.UpdateImpression(hitPoint, hit);
                    foreach (SignalRConnection connection in player.connections)
                    {
                        Clients.Client(connection.connectionId).Attacked(x, y, false);
                    }
                }
            }
        }

        #endregion


        #region InGame

        public void Attack(int x, int y, int gameId)
        {
            Game game = db.Games.Find(gameId);
            if (game.State >= 0)
            {
                OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(p => p.connections.Any(c=>c.connectionId== Context.ConnectionId));
                OnlinePlayer oPlayer = UserHandler.usersOnline.FirstOrDefault(s => s.connections.Any(c => c.gameId == gameId) && s.playerProfileId != player.playerProfileId);
                int strikeCounter = db.Strikes.Count(g => g.GameId == gameId && g.PlayerId == player.playerProfileId);
                Strike strike = new Strike();
                strike.PlayerId = player.playerProfileId;
                strike.GameId = gameId;
                strike.Turn = strikeCounter+1;
                strike.X = Convert.ToByte(x);
                strike.Y = Convert.ToByte(y);
                db.Strikes.Add(strike);
                db.SaveChanges();
                bool hit = false;
                ShipPart shipPart = db.ShipParts.FirstOrDefault(sp => sp.Ship.GameId == gameId && sp.Ship.PlayerId == oPlayer.playerProfileId && sp.X == x && sp.Y == y);
                if (shipPart != null)
                {
                    shipPart.Destroyed = true;
                    db.SaveChanges();
                    hit = true;
                }
                bool victory = !db.ShipParts.Any(shp => shp.Ship.GameId == gameId && shp.Ship.PlayerId == oPlayer.playerProfileId && shp.Destroyed == false);
                if (victory)
                {
                    Message("a4e01db2-4923-4426-ace6-9bd0de35dacc", game.GameId, game.Players.FirstOrDefault(p=> p.PlayerId==player.playerProfileId).GamerTag+" is victorious!");
                    game.WinnerId = player.playerProfileId;
                }
                foreach (SignalRConnection connection in player.connections)
                {
                    Clients.Client(connection.connectionId).Attack(x, y, hit, victory);
                }
                foreach (SignalRConnection connection in oPlayer.connections)
                {
                    Clients.Client(connection.connectionId).Attacked(x, y, victory);
                }
            }
            else
            {
                Player botdb = db.Players.FirstOrDefault(j => j.GamerTag == "KimJongFun");
                OnlinePlayer player = UserHandler.usersOnline.FirstOrDefault(p => p.connections.Any(c => c.connectionId == Context.ConnectionId));
                int strikeCounter = db.Strikes.Count(g => g.GameId == gameId && g.PlayerId == player.playerProfileId);
                Strike strike = new Strike();
                strike.PlayerId = player.playerProfileId;
                strike.GameId = gameId;
                strike.Turn = strikeCounter+1;
                strike.X = Convert.ToByte(x);
                strike.Y = Convert.ToByte(y);
                db.Strikes.Add(strike);
                db.SaveChanges();
                ShipPart shipPart = db.ShipParts.FirstOrDefault(sp => sp.Ship.GameId == gameId && sp.Ship.PlayerId == botdb.PlayerId && sp.X == x && sp.Y == y);
                bool hit = false;
                if (shipPart != null)
                {
                    shipPart.Destroyed = true;
                    hit = true;
                    db.SaveChanges();
                }
                bool victory = !db.ShipParts.Any(shp => shp.Ship.GameId == gameId && shp.Ship.PlayerId == botdb.PlayerId && shp.Destroyed == false);
                if (victory)
                {
                    Message("a4e01db2-4923-4426-ace6-9bd0de35dacc", game.GameId, game.Players.FirstOrDefault(p => p.PlayerId == player.playerProfileId).GamerTag + ", you cheated!");
                    game.WinnerId = player.playerProfileId;
                }
                foreach (SignalRConnection connection in player.connections)
                {
                    Clients.Client(connection.connectionId).Attack(x, y, hit, victory);
                }
                Bot bot = UserHandler.bots.FirstOrDefault(g=>g.opponentId==player.playerProfileId && g.gameId==gameId);
                Point hitPoint = bot.Fire();
                x = hitPoint.X;
                y = hitPoint.Y;
                strikeCounter = db.Strikes.Count(g => g.GameId == gameId && g.PlayerId == player.playerProfileId);
                strike = new Strike();
                strike.PlayerId = botdb.PlayerId;
                strike.GameId = gameId;
                strike.Turn = strikeCounter+1;
                strike.X = Convert.ToByte(x);
                strike.Y = Convert.ToByte(y);
                db.Strikes.Add(strike);
                db.SaveChanges();
                shipPart = db.ShipParts.FirstOrDefault(sp => sp.Ship.GameId == gameId && sp.Ship.PlayerId == player.playerProfileId && sp.X == x && sp.Y == y);
                hit = false;
                if (shipPart != null)
                {
                    shipPart.Destroyed = true;
                    hit = true;
                    db.SaveChanges();
                }
                bot.UpdateImpression(hitPoint, hit);
                victory = !db.ShipParts.Any(shp => shp.Ship.GameId == gameId && shp.Ship.PlayerId == player.playerProfileId && shp.Destroyed == false);
                if (victory)
                {
                    Message("a4e01db2-4923-4426-ace6-9bd0de35dacc", game.GameId, "Victory is mine, as always!");
                    game.WinnerId = player.playerProfileId;
                }
                foreach (SignalRConnection connection in player.connections)
                {
                    Clients.Client(connection.connectionId).Attacked(x, y, victory);
                }
            }
        }

        public void Message(string myId, int gameId, string message)
        {
            Game game = db.Games.Find(gameId);
            List<OnlinePlayer> playersInGame = UserHandler.usersOnline.Where(x => x.connections.Any(c => c.gameId == game.GameId)).ToList();

            ChatM chatMdb = new ChatM();
            chatMdb.FromPlayerId = db.Players.FirstOrDefault(p => p.UserId == myId).PlayerId;
            chatMdb.ToPlayerId = 1;
            chatMdb.Message = message;
            chatMdb.Time = DateTime.Now;
            chatMdb.GameId = gameId;
            db.ChatMs.Add(chatMdb);
            db.SaveChanges();

            ChatMessage chatMessage = new ChatMessage();
            chatMessage.message = message;
            chatMessage.time = DateTime.Now.ToString("hh:mm:ss");
            chatMessage.img = "/images/" + myId + ".jpg";
            chatMessage.sender = db.Players.FirstOrDefault(x => x.UserId == myId).GamerTag;
            string tst = Context.ConnectionId;
            String messageView = PartialViewToStringHelper.GetRazorViewAsString(chatMessage, "~/Views/Shared/Partial/_Message.cshtml");
            foreach (OnlinePlayer player in playersInGame)
            {
                foreach (SignalRConnection connection in player.connections)
                {
                    Clients.Client(connection.connectionId).Message(messageView, game.GameId);
                }
            }
        }
        

        #endregion


    }
}